package demo1;

import org.junit.Test;

import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @description:反射
 * @author:Mr.Cat
 * @create:2021-11-18 09:27
 **/
public class ReflectionTest {
    //反射前，对应Person的操作
    @Test
    public void test1() {
        //实例化Person对象
        Person p1 = new Person("李雄", 22);

        //通过对象，调用其内部的属性、方法
        p1.age = 18;
        System.out.println(p1.toString());

        p1.show();

        //在Person类外部，不可以通过Person类的对象调用其私有的结构。
        //比如：name,showNation()以及私有的构造器

    }

    //反射后，对应Person的操作
    @Test
    public void test2() throws Exception {
        Class<Person> pClass = Person.class;
        //通过反射，创建Person类的对象
        Constructor cons = pClass.getConstructor(String.class, int.class);
        Object obj = cons.newInstance("Tom", 18);
        System.out.println(obj.toString());

        //通过反射，调用指定的属性和方法
        Field age = pClass.getDeclaredField("age");
        age.set(obj,10);
        System.out.println(obj.toString());

        //调用方法
        Method show = pClass.getDeclaredMethod("show");
        show.invoke(obj);
        System.out.println("----------");
        //调私有结构：比如私有的构造器、方法、属性
        Constructor cons1 = pClass.getDeclaredConstructor(String.class);
        cons1.setAccessible(true);
        Object jack = cons1.newInstance("Jack");
        System.out.println(jack);

        //调用私有的属性
        Field name = pClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(jack,"长得美");
        System.out.println(jack);
        //调用私有的方法
        Method showNation = pClass.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        String nation = (String)showNation.invoke(jack, "China");
        //System.out.println(showNation.invoke(jack, "China"));//相当于String nation = jack.showNation("中国");

        //疑问1:公共结构可以使用反射和直接new的方式，开发中用哪个？
        //建议：直接new的方式。
        //什么时候用反射：反射的特征：动态性
        //疑问2：反射机制与面向对象中的封装性是不是矛盾的？如何看待两个技术？
        //不矛盾
    }
    /*
     * 关于java.lang.Class类的理解
     * 1.类的加载过程：
     *   程序经过javac.exe命令以后，会生成一个或多个字节码文件（.class结尾），借助我们使用
     *   java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。
     *   此过程就称为类的加载。加载到内存中的类，我们就称为运行时类，
     *   此类运行时类，就作为Class的一个实例。
     * 2.换句话说，Class的实例就对应着一个运行时类
     * 3.加载到内存中的运行时类，会缓存一定的时间。在此时间之内，我们可以通过不同的方式来获取此运行时类。
     * */
    //获取Class的实例的方式
    @Test
    public void test3()throws Exception{
        //方式一：
        Class<Person> clazz1= Person.class;
        System.out.println(clazz1);
        //方式二：通过运行时类的对象，调用getClass（）
        Person p1 = new Person();
        Class clazz2=p1.getClass();
        System.out.println(clazz2);
        //方式三：调用Class的静态方法：forName（String classsPath）
        Class clazz3 = Class.forName("demo1.Person");

        System.out.println(clazz3);
        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);

        //方式四：使用类的加载器：ClassLoader
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("demo1.Person");
        System.out.println(clazz4);
    }

    //Class实例可以是那些结构的说明
    @Test
    public void test4(){
        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class c3 = String.class;
        Class c4 = int[][].class;
        Class c5 = ElementType.class;
        Class c6 = Override.class;
        Class c7 = int.class;
        Class c8 = void.class;
        Class c9 = Class.class;

        int[] a = new int[10];
        int[] b = new int[100];
        Class c10 = a.getClass();
        Class c11 = b.getClass();

        //只要元素类型与维度一样，就是同一个Class
        System.out.println(c10 == c11);
    }
    @Test
    public void testPrivate() throws Exception{
        Class clazz = User.class;
        Constructor constructor = clazz.getConstructor(String.class,String.class);
        Object object = constructor.newInstance("lx", "男");
        System.out.println(object.toString());

        Class clazz1=User.class;
        Field def = clazz1.getDeclaredField("sex");
        Object obj1=clazz1.newInstance();
        def.setAccessible(true);
        def.set(obj1,"李雄");

        System.out.println(obj1.toString());
    }
}

